﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Apollo;
using NHibernate.Mapping;
using NHibernate;
using Apollo.Web;

namespace Apollo.Website
{
    public partial class EntityManager : System.Web.UI.Page, IApolloPage
    {
        IEnumerable<Entity> entities;
        IEnumerable<Apollo.MenuItem> menuItems;
        IEnumerable<EntityProperty> properties;

        protected void Page_Load(object sender, EventArgs e)
        {
            LoadEntities();

            if (!Page.IsPostBack)
            {
                CreateSQLScripts();
            }
        }

        protected void LoadEntities()
        {
            entities = Apollo.Application.CurrentSession
                .QueryOver<Entity>()
                .List<Entity>();

            menuItems = Apollo.Application.CurrentSession
                .QueryOver<Apollo.MenuItem>()
                .List<Apollo.MenuItem>();

            properties = Apollo.Application.CurrentSession
                .QueryOver<EntityProperty>()
                .List<EntityProperty>();
        }

        protected void CreateSQLScripts()
        {
            NHibernate.Cfg.Configuration config = Apollo.Application.Configuration;

            RepeaterEntities.DataSource = config.ClassMappings
                .Select<PersistentClass, Type>(p => p.MappedClass)
                .OrderBy<Type, string>(t => t.Name);
            RepeaterEntities.DataBind();
        }

        protected bool ExecuteDefinitionScripts(Entity entity, string entity_name, ITransaction tran, ISession session) {

            bool changesPresent = false;

            if (entity != null)
                entity_name = entity.Name;

            #region Create Entity Definition

            PersistentClass persistent = Apollo.Application.Configuration
                .ClassMappings
                .Single<PersistentClass>(p => p.MappedClass.Name == entity_name);

            entity = entities.SingleOrDefault<Entity>(en => en.Name == persistent.MappedClass.Name);

            if (entity == null)
            {
                string displayProperty = "";

                if (persistent.PropertyIterator.Any<Property>(pi => pi.Name == "Name"))
                    displayProperty = "Name";
                else if (persistent.PropertyIterator.Any<Property>(pi => pi.Name == "DisplayName"))
                    displayProperty = "DisplayName";
                else if (persistent.PropertyIterator.Any<Property>(pi => pi.Name == "Description"))
                    displayProperty = "Description";
                else displayProperty = persistent.IdentifierProperty.Name;


                // create entity
                entity = new Entity()
                {
                    Name = persistent.MappedClass.Name,
                    IdentifierProperty = persistent.IdentifierProperty.Name,
                    DisplayProperty = displayProperty,
                    Type = persistent.MappedClass.FullName
                };


                tran = session.BeginTransaction();
                session.Save(entity);
                changesPresent = true;
                tran.Commit();

                // create language definition
                LanguageDefinition entityDefinition = new LanguageDefinition()
                {
                    DisplayName = entity.Name,
                    Type = "Entity",
                    Language = Apollo.Application.DefaultLanguage,
                    CodeHi = entity.Name
                };
                tran = session.BeginTransaction();
                session.Save(entityDefinition);
                changesPresent = true;
                tran.Commit();

            }

            #endregion

            #region Create Properties Definitions

            foreach (Property p in persistent.PropertyIterator)
            {
                EntityProperty eProperty = properties.SingleOrDefault<EntityProperty>(ep => ep.Entity.Id == entity.Id && ep.PropertyName == p.Name);

                if (eProperty == null)
                {
                    eProperty = new EntityProperty()
                    {
                        Entity = entity,
                        PropertyName = p.Name,
                        Type = p.IsEntityRelation ? EntityPropertyType.Relation : EntityPropertyType.Normal
                    };

                    if (eProperty.Type == EntityPropertyType.Relation)
                    {
                        Entity belongsTo = entities.SingleOrDefault<Entity>(en => en.Name == p.PersistentClass.MappedClass.GetProperty(p.Name).PropertyType.Name);

                        if (belongsTo != null)
                            eProperty.BelongToEntity = belongsTo;
                    }

                    tran = session.BeginTransaction();
                    session.Save(eProperty);
                    changesPresent = true;
                    tran.Commit();

                    LanguageDefinition propertyDefinition = Apollo.Application.CurrentSession
                        .QueryOver<LanguageDefinition>()
                        .Where(ld => ld.CodeHi == entity.Name && ld.CodeLo == eProperty.PropertyName)
                        .SingleOrDefault();

                    if(propertyDefinition == null){
                        propertyDefinition = new LanguageDefinition()
                        {
                            DisplayName = eProperty.PropertyName,
                            Type = "EntityProperty",
                            Language = Apollo.Application.DefaultLanguage,
                            CodeHi = entity.Name,
                            CodeLo = eProperty.PropertyName
                        };
                        tran = session.BeginTransaction();
                        session.Save(propertyDefinition);
                        changesPresent = true;
                        tran.Commit();
                    }
                }
                else if (eProperty.Type == EntityPropertyType.Relation && eProperty.BelongToEntity == null)
                {
                    Entity belongsTo = entities.SingleOrDefault<Entity>(en => en.Name == p.PersistentClass.MappedClass.GetProperty(p.Name).PropertyType.Name);

                    if (belongsTo != null)
                    {
                        eProperty.BelongToEntity = belongsTo;
                        tran = session.BeginTransaction();
                        session.Save(eProperty);
                        changesPresent = true;
                        tran.Commit();
                    }
                }
            }

            // for identity
            EntityProperty eIDProperty = properties.SingleOrDefault<EntityProperty>(ep => ep.Entity.Id == entity.Id && ep.PropertyName == persistent.IdentifierProperty.Name);
            if (eIDProperty == null)
            {
                eIDProperty = new EntityProperty()
                {
                    Entity = entity,
                    PropertyName = persistent.IdentifierProperty.Name,
                    Type = EntityPropertyType.Normal
                };

                tran = session.BeginTransaction();
                session.Save(eIDProperty);
                changesPresent = true;
                tran.Commit();

                LanguageDefinition eIDPropertyDefinition = Apollo.Application.CurrentSession
                    .QueryOver<LanguageDefinition>()
                    .Where(ld => ld.CodeHi == entity.Name && ld.CodeLo == eIDProperty.PropertyName)
                    .SingleOrDefault();

                if (eIDPropertyDefinition == null)
                {
                    eIDPropertyDefinition = new LanguageDefinition()
                    {
                        DisplayName = eIDProperty.PropertyName,
                        Type = "EntityProperty",
                        Language = Apollo.Application.DefaultLanguage,
                        CodeHi = entity.Name,
                        CodeLo = eIDProperty.PropertyName
                    };
                    tran = session.BeginTransaction();
                    session.Save(eIDPropertyDefinition);
                    changesPresent = true;
                    tran.Commit();
                }
            }


            foreach (EntityProperty p in properties.Where<EntityProperty>(ep => ep.Entity.Id == entity.Id))
            {
                if (!persistent.PropertyIterator.Any<Property>(prop => prop.Name == p.PropertyName)
                    && persistent.IdentifierProperty.Name != p.PropertyName)
                {
                    tran = session.BeginTransaction();
                    session.Delete(p);
                    changesPresent = true;
                    tran.Commit();
                }
            }

            #endregion

            return changesPresent;
        }

        protected void ButtonSave_Click(object sender, EventArgs e)
        {
            ISession session = Apollo.Application.CurrentSession;
            if (session.QueryOver<Language>().List<Language>().Count<Language>() <= 0)
            {
                LabelWarn.Text = "No language present. First create a language!";
                LabelWarn.Visible = true;
                ImageWarn.ImageUrl = "/css/icons/error.png";
                ImageWarn.Visible = true;
                return;
            }

            ITransaction tran = session.BeginTransaction();
            try
            {
                bool changesPresent = true; // set initial value
                int changesCounter = 0;

                while (changesPresent && changesCounter < 100)
                {
                    changesCounter++;
                    changesPresent = false; // reset counter

                    foreach (RepeaterItem ri in RepeaterEntities.Items)
                    {
                        Label LabelEntity = ri.FindControl("LabelEntity") as Label;
                        CheckBox CheckBoxEntity = ri.FindControl("CheckBoxEntity") as CheckBox;
                        CheckBox CheckBoxDefaultView = ri.FindControl("CheckBoxDefaultView") as CheckBox;
                        CheckBox CheckBoxMenuItem = ri.FindControl("CheckBoxMenuItem") as CheckBox;

                        if (LabelEntity == null) continue;

                        Entity entity = entities
                            .FirstOrDefault<Entity>(en => en.Name == LabelEntity.Text);

                        if (CheckBoxEntity.Checked)
                        {
                            changesPresent = changesPresent || ExecuteDefinitionScripts(entity, LabelEntity.Text, tran, session);

                            if (changesPresent) { 
                                // recalculate entities and properties
                                entities = Apollo.Application.CurrentSession
                                    .QueryOver<Entity>()
                                    .List<Entity>();

                                properties = Apollo.Application.CurrentSession
                                    .QueryOver<EntityProperty>()
                                    .List<EntityProperty>();
                            }
                            CheckBoxEntity.Enabled = false;

                        }

                        if (CheckBoxDefaultView.Checked)
                        {
                            #region Create entity view

                            PersistentClass persistent = Apollo.Application.Configuration
                                .ClassMappings
                                .Single<PersistentClass>(p => p.MappedClass.Name == LabelEntity.Text);

                            string viewString = persistent.IdentifierProperty.Name;

                            if (entity.DisplayProperty != viewString)
                            {
                                viewString += "," + entity.DisplayProperty;
                            }

                            int maxProperties = 3;
                            foreach (Property p in persistent.PropertyIterator)
                            {
                                if (p.Name == entity.DisplayProperty)
                                    continue;

                                viewString += "," + p.Name;

                                maxProperties--;
                                if (maxProperties <= 0)
                                    break;
                            }

                            tran = session.BeginTransaction();

                            EntityView view = new EntityView()
                            {
                                Entity = entity,
                                Name = entity.Name + " list",
                                ViewString = viewString
                            };
                            session.Save(view);

                            entity.DefaultView = view;
                            session.Update(entity);

                            tran.Commit();

                            CheckBoxDefaultView.Enabled = false;

                            #endregion
                        }

                        if (CheckBoxMenuItem.Checked)
                        {
                            #region Create menu item

                            if (entity.DefaultView == null)
                                throw new Exception("Default view missing for entity \"" + entity.Name + "\"");

                            tran = session.BeginTransaction();

                            Apollo.MenuItem settingsMenu = Apollo.Application.Meta<Apollo.MenuItem>().Single<Apollo.MenuItem>(mi => mi.Name == "Settings");

                            Apollo.MenuItem menu = new Apollo.MenuItem()
                            {
                                DisplayOrder = 1,
                                Name = entity.Name + " list",
                                IconPath = entity.IconPath,
                                EntityView = entity.DefaultView,
                                Parent = settingsMenu
                            };
                            session.Save(menu);

                            tran.Commit();

                            CheckBoxMenuItem.Enabled = false;

                            #endregion
                        }
                    }
                }

                LabelWarn.Text = "Datas were generated";
                LabelWarn.CssClass = "warn";
                LabelWarn.Visible = true;
                ImageWarn.ImageUrl = "/css/icons/icon_38.png";
                ImageWarn.Visible = true;

            }
            catch (Exception ex)
            {
                LabelWarn.Text = "Action failed! Reason: " + ex.Message;
                LabelWarn.CssClass = "error";
                LabelWarn.Visible = true;
                ImageWarn.ImageUrl = "/css/icons/error.png";
                ImageWarn.Visible = true;
            }
        }


        protected bool ExistsInDb(string entityName)
        {
            // check entity
            if (!entities.Any<Entity>(e => e.Name == entityName))
                return false;

            // check property
            PersistentClass persistent = Apollo.Application.Configuration
                            .ClassMappings
                            .Single<PersistentClass>(p => p.MappedClass.Name == entityName);
            Entity entity = entities.Single<Entity>(en => en.Name == entityName);

            foreach (Property p in persistent.PropertyIterator)
            {
                if (!properties.Any<EntityProperty>(ep => ep.PropertyName == p.Name && ep.Entity.Id == entity.Id))
                    return false;
            }

            if (!properties.Any<EntityProperty>(ep => ep.PropertyName == persistent.IdentifierProperty.Name && ep.Entity.Id == entity.Id))
                return false;

            if (properties.Any<EntityProperty>(ep => ep.Type == EntityPropertyType.Relation && ep.BelongToEntity == null && ep.Entity.Id == entity.Id))
                return false;

            return true;
        }

        protected bool HasDefaultView(string entityName)
        {
            if (!entities.Any<Entity>(e => e.Name == entityName))
                return false;

            return null != entities.First<Entity>(e => e.Name == entityName).DefaultView;
        }

        protected bool HasMenuItem(string entityName)
        {
            if (!this.HasDefaultView(entityName))
                return false;

            Entity entity = entities.FirstOrDefault<Entity>(e => e.Name == entityName);

            if (entity == null)
                return false;

            return null != menuItems.FirstOrDefault<Apollo.MenuItem>(mi => mi.EntityView != null && mi.EntityView.Entity.Id == entity.Id);
        }


        public int SelectedMenuItemID
        {
            get { return Apollo.Application.Meta<Apollo.MenuItem>().Single<Apollo.MenuItem>(mi => mi.Name == "Entity manager").Id; }
        }

        public int SelectedRootMenuItemId
        {
            get { return Apollo.Application.Meta<Apollo.MenuItem>().Single<Apollo.MenuItem>(mi => mi.Name == "Settings").Id; }
        }
    }
}